package com.szq.lottery.domain.strategy.service.draw;

import com.szq.lottery.domain.strategy.model.aggregates.StrategyRich;
import com.szq.lottery.domain.strategy.model.reg.DrawReq;
import com.szq.lottery.domain.strategy.model.res.DrawResult;
import com.szq.lottery.domain.strategy.service.alogorithm.IDrawAlgorithm;
import com.szq.lottery.common.Constants;
import com.szq.lottery.domain.strategy.model.vo.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * 定义抽奖过程，模版模式
 * @author 吾空白
 */
public abstract class AbstractDrawBase extends DrawStrategySupport implements IDrawExec{

    private Logger logger= LoggerFactory.getLogger(AbstractDrawBase.class);


    @Override
    public DrawResult doDrawExec(DrawReq req) {
        //1.获取抽奖策略
        StrategyRich strategyRich = super.queryStrategyRich(req.getStrategyId());
        StrategyBriefVO strategy = strategyRich.getStrategy();

        //2.校验抽奖策略是否已经初始化到内存
        this.checkAndInitRateData(req.getStrategyId(),strategy.getStrategyMode(),strategyRich.getStrategyDetailList());

        //3.获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等
        List<String> excludeAwardIds=this.queryExcludeAwardIds(req.getStrategyId());

        //4.执行抽奖算法
        String awardId=this.drawAlgorithm(req.getStrategyId(),drawAlgorithmGroup.get(strategy.getStrategyMode()),excludeAwardIds);

        //5.包装中奖结果
        return buildDrawResult(req.getuId(),req.getStrategyId(),awardId,strategy);

    }


    /**
     * 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等，这类数据是含有业务逻辑的，所以需要由具体的实现决定
     * @param strategyId 策略ID集合
     * @return 排除的奖品ID
     */
    protected abstract List<String> queryExcludeAwardIds(Long strategyId);


    /**
     * 执行抽奖算法
     * @param strategyId 策略ID
     * @param iDrawAlgorithm 抽奖算法模型
     * @param excludeAwardIds 排除的抽奖ID集合
     * @return
     */
    protected abstract String drawAlgorithm(Long strategyId, IDrawAlgorithm iDrawAlgorithm, List<String> excludeAwardIds);


    /**
     * 校验抽奖策略是否已经初始化到内存
     *  @param strategyId         抽奖策略ID
     * @param strategyModel       抽奖策略模式
     * @param strategyDetailList 抽奖策略详情
     */
    private void checkAndInitRateData(Long strategyId, Integer strategyModel, List<StrategyDetailBriefVO> strategyDetailList){
        IDrawAlgorithm drawAlgorithm = drawAlgorithmGroup.get(strategyModel);

        List<AwardRateInfo> awardRateInfoList = new ArrayList<>(strategyDetailList.size());
        for (StrategyDetailBriefVO strategyDetail: strategyDetailList){
            awardRateInfoList.add(new AwardRateInfo(strategyDetail.getAwardId(),strategyDetail.getAwardRate()));
        }

        // 非单项概率，不必存入缓存
        if (!Constants.StrategyMode.SINGLE.getCode().equals(strategyModel)) {
            drawAlgorithm.initRateTupe(strategyId,awardRateInfoList);
            return;
        }

        boolean existRateTupe = drawAlgorithm.isExistRateTupe(strategyId);
        if (existRateTupe) {
            return;
        }


        drawAlgorithm.initRateTupe(strategyId,awardRateInfoList);
    }

    private  DrawResult buildDrawResult(String uId, Long strategyId, String awardId,StrategyBriefVO strategy){
        if(null==awardId){
            logger.info("执行策略抽奖完成【未中奖】，用户：{} 策略ID：{}",uId,strategyId);
            return new DrawResult(uId,strategyId,Constants.DrawState.FAIL.getCode());
        }
        AwardBriefVO award = super.queryAwardInfoByAwardId(awardId);
        DrawAwardInfo drawAwardInfo = new DrawAwardInfo(uId, award.getAwardId(), award.getAwardType(), award.getAwardName(), award.getAwardContent());
        drawAwardInfo.setStrategyMode(strategy.getStrategyMode());
        drawAwardInfo.setGrantType(strategy.getGrantType());
        drawAwardInfo.setGrantDate(strategy.getGrantDate());
        logger.info("执行策略抽奖完成【已中奖】,用户：{} 策略ID:{} 奖品ID:{} 奖品名称：{}",uId,strategyId,awardId,award.getAwardName());

        return new DrawResult(uId,strategyId,Constants.DrawState.SUCCESS.getCode(),drawAwardInfo);

    }
}
